#include "gamewindow.h"
#include "ui_gamewindow.h"
#include "world.h"
#include "labelmanager.h"
#include <QStringList>
#include <QString>
#include <QDebug>
#include <QLabel>
#include <QPixmap>
#include <math.h>
#include <QDebug>

GameWindow::GameWindow(GameSettings *s, Highscores *h, QWidget *parent) :
    settings(s), highscores(h), QMainWindow(parent),
    ui(new Ui::GameWindow)
{
    ui->setupUi(this);

    World::getWorld().attachObserver(this);


    connect(settings, SIGNAL(gameStarted()), this, SLOT(startGame()));

    // Timers
    gameTimer = new QTimer(this);   // main timer; responsible for all the label-movements
    gameTimer->setInterval(MAIN_TIMER_UPDATE_INTERVAL);
    connect(gameTimer, &QTimer::timeout, this, &GameWindow::gameUpdate);

    bananaTimer = new QTimer(this); // responsible for the interval between two falling bananas
    connect(bananaTimer, &QTimer::timeout, this, &GameWindow::bananaTimerHit);

    // Networking
    connect(settings, SIGNAL(toGameWindow_connectionLost()), this, SLOT(connectionLost()));
    connect(settings, SIGNAL(toGameWindow_ClientReceivedData(QString)), this, SLOT(_client_receivedData(QString)));
    connect(settings, SIGNAL(toGameWindow_ServerReceivedData(QString)), this, SLOT(_server_receivedData(QString)));
    connect(this, SIGNAL(fromGameWindow_sendToClient(QString)), settings, SLOT(_server_sendToClient(QString)));
    connect(this, SIGNAL(fromGameWindow_sendToServer(QString)), settings, SLOT(_client_sendToServer(QString)));

    time = 0;
    gamePaused = true;
    gameInProgress = false;

}

GameWindow::~GameWindow()
{
    delete ui;
}


void GameWindow::setGamePaused(bool b)
{
    gamePaused = b;
    if (gamePaused) {
        gameTimer->stop();
        bananaTimer->stop();
    } else {
        gameTimer->start();
        bananaTimer->start();
    }
}

void GameWindow::on_btnMenu_clicked()
{
    if (settings->getPlayer() == CLIENT && gameInProgress)
    {
        _client_sendToServer("gotomenu");
        setGamePaused(true);
        gameInProgress = false;
        emit updateWindowState(true, false, true, false);
        settings->setRightPlayerIsReady(false);
        settings->updateWidgetState();
    }
    else if (settings->getGameMode() == SINGLE || settings->getGameMode() == LOCAL)
    {
        setGamePaused(true);
        emit updateWindowState(true, false, false, false);
    }
    else if (settings->getPlayer() == HOST && gameInProgress)
    {
        setGamePaused(true);
        gameInProgress = false;
        World::getWorld().reset();
        emit updateWindowState(true, false, true, false);
        settings->setRightPlayerIsReady(false);
        settings->updateWidgetState();
        _server_sendToClient("gotomenu");
    }
}

void GameWindow::keyPressEvent(QKeyEvent* ev) {
    if (gamePaused) { return; }

    if (ev->isAutoRepeat()) {
        ev->ignore();
    } else {
        switch (ev->key())
        {
            case Qt::Key_Q:
                ev->accept();
                if (settings->getPlayer() == HOST && !monkeyOneFalling)
                {
                    World::getWorld().setMonkeyMoveState(HOST, true, UP);
                }
                else if (settings->getPlayer() == CLIENT)
                {
                    _client_sendToServer("keypress:up");
                }
                break;
            case Qt::Key_A:
                ev->accept();
                if (settings->getPlayer() == HOST && !monkeyOneFalling)
                {
                    World::getWorld().setMonkeyMoveState(HOST, true, DOWN);
                }
                else if (settings->getPlayer() == CLIENT)
                {
                    _client_sendToServer("keypress:down");
                }
                break;
            case Qt::Key_S:
                ev->accept();
                if (settings->getPlayer() == HOST && !monkeyOneFalling)
                {
                    shootCoconut(HOST);
                }
                else if (settings->getPlayer() == CLIENT)
                {
                    _client_sendToServer("keypress:shoot");
                }
                break;
            case Qt::Key_Up:   // need to set focus when pulling up gamewindow in order to have arrow keys working
                if (settings->getGameMode() == LOCAL && !monkeyTwoFalling) {
                    ev->accept();
                    World::getWorld().setMonkeyMoveState(CLIENT, true, UP);
                }
                break;
            case Qt::Key_Down:
                if (settings->getGameMode() == LOCAL && !monkeyTwoFalling) {
                    ev->accept();
                    World::getWorld().setMonkeyMoveState(CLIENT, true, DOWN);
                }
                break;
            case Qt::Key_Control:
                if (settings->getGameMode() == LOCAL && !monkeyTwoFalling) {
                    ev->accept();
                    shootCoconut(CLIENT);
                }
                break;
            default:
                ev->ignore();
                break;
        }
    }
}

void GameWindow::keyReleaseEvent(QKeyEvent* ev) {
    if (gamePaused) { return; }

    if (ev->isAutoRepeat()) {
        ev->ignore();
    } else {
        switch (ev->key())
        {
            case Qt::Key_Q:
            case Qt::Key_A:
                ev->accept();
                if (settings->getPlayer() == HOST && !monkeyOneFalling)
                {
                    World::getWorld().setMonkeyMoveState(HOST, false);
                }
                else if (settings->getPlayer() == CLIENT)
                {
                    _client_sendToServer("keyrelease:downup");
                }
                break;
            case Qt::Key_S:
                ev->accept();
                break;
            case Qt::Key_Up:
            case Qt::Key_Down:
                if (settings->getGameMode() == LOCAL && !monkeyTwoFalling) {
                    ev->accept();
                    World::getWorld().setMonkeyMoveState(CLIENT, false);
                }
                break;
            case Qt::Key_Control:
                if (settings->getGameMode() == LOCAL && !monkeyTwoFalling) {
                    ev->accept();
                }
                break;
            default:
                ev->ignore();
                break;
        }
    }
}

void GameWindow::gameUpdate()
{
    if (!gamePaused) {
        if (settings->getGameMode() == SINGLE) {
            World::getWorld().doMonkeyAIMove();
        }

        World::getWorld().updateWorld();

        time += MAIN_TIMER_UPDATE_INTERVAL;

        double percent = 1.0 * time / (settings->getDuration() * 1000 * 60);
        moveSun(percent);

        moveProgressBars();

        if (time >= settings->getDuration() * 1000 * 60)
        {
            gameOver();
        }
    }
}

void GameWindow::bananaTimerHit()
{
    if (!gamePaused) {
        World::getWorld().createBanana();
        bananaTimer->setInterval(MIN_BANANA_INTERVAL.at(_difficulty_) + rand()
                                 % (MAX_BANANA_INTERVAL.at(_difficulty_) - MIN_BANANA_INTERVAL.at(_difficulty_) + 1));
    }
}

void GameWindow::closeEvent(QCloseEvent* event)
{
    (void)event;

    if (settings->getGameMode() == NETWORK && gameInProgress) {
        on_btnMenu_clicked();
    }
    QApplication::processEvents();
    QApplication::quit();
}


void GameWindow::startGame()
{
    emit updateWindowState(false, true, false, false);

    resetGameStatus();

    if (settings->getPlayer() == HOST)
    {
        ui->lblName1->setText(settings->getPlayer1Name());
        ui->lblName2->setText(settings->getPlayer2Name());

        bananaTimer->setInterval(MIN_BANANA_INTERVAL.at(_difficulty_) + rand()
                                 % (MAX_BANANA_INTERVAL.at(_difficulty_) - MIN_BANANA_INTERVAL.at(_difficulty_) + 1));

        gameTimer->start();
        bananaTimer->start();
    }

    gamePaused = false;
    gameInProgress = true;
}

void GameWindow::gameOver()
{
    gameInProgress = false;
    setGamePaused(true);
    settings->setRightPlayerIsReady(false);
    settings->updateWidgetState();

    if (settings->getPlayer() == HOST)
    {
        World::getWorld().reset();

        if (_difficulty_ != CHEAT)
        {
            highscores->addHighscores(settings->getPlayer1Name(), player1Score, settings->getDuration());

            if (settings->getGameMode() != SINGLE) {
                highscores->addHighscores(settings->getPlayer2Name(), player2Score, settings->getDuration());
            }

            if (settings->getConnected() && settings->getGameMode() == NETWORK) {
                _server_sendToClient("addhighscore:" + settings->getPlayer1Name() + ":" +
                                     QString::number(player1Score) + ":" + QString::number(settings->getDuration()));
                _server_sendToClient("addhighscore:" + settings->getPlayer2Name() + ":" +
                                     QString::number(player2Score) + ":" + QString::number(settings->getDuration()));
            }
        }

        if (settings->getGameMode() == NETWORK) {
            _server_sendToClient("gameover");
        }
    }

    emit updateWindowState(true, false, false, true);
}

void GameWindow::resetGameStatus()
{
    World::getWorld().reset();

    if (settings->getPlayer() == HOST)
    {
        World::getWorld().createMonkeys();
        player1Score = player2Score = 0;
        cocoTime1 = cocoTime2 = 0;
        time = 0;
        monkeyOneFalling = monkeyTwoFalling = false;
    }

    // set scores
    ui->lcdScore1->display(0);
    ui->lcdScore2->display(0);

    // set progress bars
    ui->progressBar1->setValue(100);
    ui->progressBar2->setValue(100);
    ui->progressBar1->move(MONKEY_DISTANCE_FROM_BORDER, MONKEY_LOWEST_POINT + MONKEY_HEIGHT + 5);
    ui->progressBar2->move(WORLD_WIDTH - MONKEY_DISTANCE_FROM_BORDER - MONKEY_WIDTH,
                           MONKEY_LOWEST_POINT + MONKEY_HEIGHT + 5);
    ui->progressBar1->show();
    ui->progressBar2->show();
}

void GameWindow::shootCoconut(PlayerType pl)
{
    if (pl == HOST && cocoTime1 >= COCO_SHOOT_INTERVAL.at(_difficulty_))
    {
        cocoTime1 = 0;
        World::getWorld().createCoconut(HOST);
    }
    else if (pl == CLIENT && cocoTime2 >= COCO_SHOOT_INTERVAL.at(_difficulty_))
    {
        cocoTime2 = 0;
        World::getWorld().createCoconut(CLIENT);
    }
}

void GameWindow::moveSun(double percent)    // called by gameUpdate (every time the main timer hits)
{
    // move sun
    double angle = acos(1.0 * (WORLD_WIDTH / 2 + 80) / SUN_CIRCLE_RADIUS);  // 80 is sun-width/2
    angle += (3.14159 - 2 * angle) * percent;
    int sunCenterX = (int)(WORLD_WIDTH / 2 - SUN_CIRCLE_RADIUS * cos(angle));
    int sunCenterY = (int)(SUN_MAX_Y + SUN_CIRCLE_RADIUS - SUN_CIRCLE_RADIUS * sin(angle));
    ui->lblSun->move(sunCenterX - 80, sunCenterY - 80);
    ui->lblSun->show();

    // change sky color
    int rgb1 = (int)abs(183 - (183 - 59) * abs(2 * percent - 1));
    int rgb2 = (int)abs(233 - (233 - 96) * abs(2 * percent - 1));
    int rgb3 = 255;
    ui->lblSky->setStyleSheet("background-color: rgb(" + QString::number(rgb1) +
                              "," + QString::number(rgb2) +
                              "," + QString::number(rgb3) + ")");

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("sunmove:" + QString::number(percent));
    }
}

void GameWindow::moveProgressBars()      // called by game update (every time the main timer hits)
{
    if (!monkeyOneFalling) { cocoTime1 += MAIN_TIMER_UPDATE_INTERVAL; }
    if (cocoTime1 > COCO_SHOOT_INTERVAL.at(_difficulty_)) {
        cocoTime1 = COCO_SHOOT_INTERVAL.at(_difficulty_); }
    int percent1 = 100.0 * cocoTime1 / COCO_SHOOT_INTERVAL.at(_difficulty_);

    if (!monkeyTwoFalling) { cocoTime2 += MAIN_TIMER_UPDATE_INTERVAL; }
    if (cocoTime2 > COCO_SHOOT_INTERVAL.at(_difficulty_)) {
        cocoTime2 = COCO_SHOOT_INTERVAL.at(_difficulty_); }
    int percent2 = 100.0 * cocoTime2 / COCO_SHOOT_INTERVAL.at(_difficulty_);

    ui->progressBar1->setValue(percent1);
    ui->progressBar2->setValue(percent2);

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("progressbar:" + QString::number(percent1) + ":" + QString::number(percent2));
    }
}


void GameWindow::displayNewScore(PlayerType player, int newScore)
{
    if (player == HOST) {
        ui->lcdScore1->display(newScore);
    } else if (player == CLIENT) {
        ui->lcdScore2->display(newScore);
    }

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("scorechange:" + QString::number(player) + ":" + QString::number(newScore));
    }
}


/////////////////////Networking Stuff/////////////////////

void GameWindow::connectionLost()
{
//    THIS IS NOW TAKEN CARE OF IN closeEvent OF GAMEWINDOW
//    if (gameInProgress && settings->getGameMode() == NETWORK) {
//        emit updateWindowState(true, false, true, false); }
//    setGamePaused(true);
//    gameInProgress = false;
}

void GameWindow::_client_sendToServer(QString data)
{
    if (settings->getConnected() && settings->getGameMode() == NETWORK) {
        data = "ingame:" + data;
        emit fromGameWindow_sendToServer(data);
    }
}

void GameWindow::_server_sendToClient(QString data)
{
    if (settings->getConnected() && settings->getGameMode() == NETWORK) {
        data = "ingame:" + data;
        emit fromGameWindow_sendToClient(data);
    }
}

void GameWindow::_server_receivedData(QString data)
{
    QStringList list; list << data.split(':');
    if (list.at(0) == ("keypress") && !monkeyTwoFalling)
    {
        if (list.at(1) == "up") {
            World::getWorld().setMonkeyMoveState(CLIENT, true, UP);
        } else if (list.at(1) == "down") {
            World::getWorld().setMonkeyMoveState(CLIENT, true, DOWN);
        } else if (list.at(1) == "shoot") {
            shootCoconut(CLIENT);
        }
    }
    else if (list.at(0) == "keyrelease" && !monkeyTwoFalling)
    {
        if (list.at(1) == "downup") {
            World::getWorld().setMonkeyMoveState(CLIENT, false);
        }
    }
    else if (list.at(0) == "gotomenu")
    {
        on_btnMenu_clicked();
    }
}

void GameWindow::_client_receivedData(QString data)
{
    QStringList list; list << data.split(':');
    if (list.at(0) == "move") {
        movedInModel(list.at(1).toInt(), list.at(2).toInt(), list.at(3).toInt());
    } else if (list.at(0) == "create") {
        Type tp = MONKEY;
        if (list.at(2) == "1") { tp = BANANA; } else if (list.at(2) == "2") { tp = COCONUT; }
        createdInModel(list.at(1).toInt(), tp, list.at(3), list.at(4).toInt(), list.at(5).toInt());
    } else if (list.at(0) == "destroy") {
        destroyedInModel(list.at(1).toInt());
    } else if (list.at(0) == "framechange") {
        changedFrameInModel(list.at(1).toInt(), list.at(2));
    } else if (list.at(0) == "sunmove") {
        moveSun(list.at(1).toDouble());
    } else if (list.at(0) == "progressbar") {
        ui->progressBar1->setValue(list.at(1).toInt());
        ui->progressBar2->setValue(list.at(2).toInt());
    } else if (list.at(0) == "scorechange") {
        PlayerType pl = HOST; if (list.at(1) == "1") { pl = CLIENT; }
        displayNewScore(pl, list.at(2).toInt());
    } else if (list.at(0) == "name") {
        ui->lblName1->setText(list.at(1));
        ui->lblName2->setText(list.at(2));
    } else if (list.at(0) == "addhighscore") {
        highscores->addHighscores(list.at(1), list.at(2).toInt(), list.at(3).toInt());
    } else if (list.at(0) == "gameover") {
        gameOver();
    } else if (list.at(0) == "gotomenu") {
        on_btnMenu_clicked();
    }
}


/////////////////////ModelObserver/////////////////////

void GameWindow::movedInModel(int id, int xPos, int yPos)
{
    QLabel* lbl = LabelManager::getManager().getById(id);
    lbl->move(xPos, yPos);
    lbl->show();

    if (id == 0) {
        ui->progressBar1->move(xPos, yPos + MONKEY_HEIGHT + 5);
    } else if (id == 1) {
        ui->progressBar2->move(xPos, yPos + MONKEY_HEIGHT + 5);
    }

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("move:" + QString::number(id) + ":"
                     + QString::number(xPos) + ":" + QString::number(yPos));
    }
}

void GameWindow::destroyedInModel(int id)
{
    LabelManager::getManager().destroyById(id);

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("destroy:" + QString::number(id));
    }
}

void GameWindow::createdInModel(int id, Type type, QString img, int xPos, int yPos)
{
    QLabel* label = new QLabel(this);
    if (type == BANANA)
    {
        label->setGeometry(xPos, yPos, BANANA_WIDTH, BANANA_HEIGHT);
    }
    else if (type == COCONUT)
    {
        label->setGeometry(xPos, yPos, COCO_WIDTH, COCO_HEIGHT);
    }
    else if (type == MONKEY)
    {
        label->setGeometry(xPos, yPos, MONKEY_WIDTH, MONKEY_HEIGHT);
    }
    QPixmap pix(":/Images/" + img);
    label->setPixmap(pix);
    label->setScaledContents(true);
    LabelManager::getManager().addLabel(label, id);
    label->show();

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("create:" + QString::number(id) + ":" + QString::number(type)+ ":"
                     + img + ":" + QString::number(xPos) + ":" + QString::number(yPos));
    }
}

void GameWindow::changedFrameInModel(int id, QString newFrame)
{
    QLabel* lbl = LabelManager::getManager().getById(id);
    QPixmap img(":/Images/" + newFrame);
    lbl->setPixmap(img);
    lbl->show();

    if (settings->getPlayer() == HOST && settings->getConnected() && settings->getGameMode() == NETWORK)
    {
        _server_sendToClient("framechange:" + QString::number(id) + ":" + newFrame);
    }
}

void GameWindow::increasedScoreInModel(PlayerType player, int n)
{
    if (player == HOST) {
        player1Score += n;
        displayNewScore(player, player1Score);
    } else if (player == CLIENT) {
        player2Score += n;
        displayNewScore(player, player2Score);
    }
}

void GameWindow::setMonkeyToFalling(PlayerType player, bool falling)
{
    if (player == HOST)
    {
        if (falling) {
            monkeyOneFalling = true;
            cocoTime1 = 0;
        } else {
            monkeyOneFalling = false;
        }
    }
    else if (player == CLIENT)
    {
        if (falling) {
            monkeyTwoFalling = true;
            cocoTime2 = 0;
        } else {
            monkeyTwoFalling = false;
        }
    }
}

void GameWindow::monkeyAIShootsCoconut()
{
    shootCoconut(CLIENT);
}


